package org.jetbrains.kotlinconf.ui.components.zoomable

import androidx.compose.runtime.Immutable
import androidx.compose.runtime.Stable
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.TransformOrigin
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.layout.ScaleFactor
import org.jetbrains.kotlinconf.ui.components.zoomable.internal.Zero

/**
 * Graphics transformations generated by [Modifier.zoomable] when pan & zoom gestures are received.
 *
 * By default, these are automatically applied by `Modifier.zoomable()` to its content, but this
 * can be disabled using [ZoomableState.autoApplyTransformations] if you prefer applying them in
 * a bespoke manner.
 */
@Immutable
interface ZoomableContentTransformation {
  /**
   * Whether this object contains non-placeholder values. This will be false when
   * [Modifier.zoomable] is initializing and sufficient information to position
   * and show its content is not yet available.
   *
   * Until specified transformations are received, zoomable content should stay hidden.
   * This is done automatically if [ZoomableState.autoApplyTransformations] is `true`.
   */
  val isSpecified: Boolean

  /**
   * The size of the zoomable content that is currently known to [Modifier.zoomable].
   * This is calculated using the value given to [ZoomableState.setContentLocation] and may be
   * one frame behind the UI. Useful for synchronizing other elements with the zoomable content.
   */
  val contentSize: Size

  /**
   * The scale of the zoomable content along the x and y axes, generated in response
   * to zoom gestures. This value represents the scaling factor that should be applied
   * to the content to achieve the desired zoom level.
   *
   * Values on both the axes can be less than 0 if the image size is larger than
   * its layout size.
   */
  val scale: ScaleFactor

  val scaleMetadata: ScaleMetadata

  /**
   * The rotation of the zoomable content around the z-axis. This parameter is currently
   * unused and defaults to `0`, but may be used in the future if [Modifier.zoomable]
   * adds support for rotation gestures.
   */
  val rotationZ: Float

  /**
   * The translation of the zoomable content along the x and y axes, generated in
   * response to pan gestures. This value represents the amount by which the content
   * should be translated to achieve the desired pan position.
   */
  val offset: Offset

  /**
   * Offset percentage along the x and y axis for which [scale] and [offset] must be applied.
   */
  val transformOrigin: TransformOrigin get() = TransformOrigin.Zero

  /**
   * Central point around which the last pan/zoom gesture was made with respect to the
   * layout bounds. Will be unspecified if the content hasn't been interacted with yet.
   */
  val centroid: Offset?

  interface ScaleMetadata {
    /**
     * The default scale needed to position the content within its layout bounds with
     * respect to [ZoomableState.contentScale].
     */
    val initialScale: ScaleFactor

    /**
     * Scale applied by the user using zoom gestures. The final scale of the content
     * is calculated using [initialScale] x [userZoom].
     */
    val userZoom: Float
  }
}

@Stable
internal fun Modifier.applyTransformation(transformation: ZoomableContentTransformation): Modifier {
  return graphicsLayer {
    scaleX = transformation.scale.scaleX
    scaleY = transformation.scale.scaleY
    rotationZ = transformation.rotationZ
    translationX = transformation.offset.x
    translationY = transformation.offset.y
    transformOrigin = transformation.transformOrigin
  }
}
